package springframework.beans.factory.support;

import springframework.beans.BeanWrapper;
import springframework.beans.PropertyValue;
import springframework.beans.PropertyValues;
import springframework.beans.exceptions.BeansException;
import springframework.beans.exceptions.FatalBeanException;
import springframework.beans.exceptions.NoSuchBeanDefinitionException;
import springframework.beans.factory.BeanFactory;
import springframework.beans.factory.config.BeanPostProcessor;
import springframework.beans.factory.config.ConfigurableBeanFactory;

import java.beans.PropertyEditor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public abstract class AbstractBeanFactory implements ConfigurableBeanFactory {

    public static final String FACTORY_BEAN_PREFIX = "&";
    private BeanFactory parentBeanFactory;
    private Map customEditors;
    private final List<BeanPostProcessor> beanPostProcessors;
    private final Set ignoreDependencyTypes;
    private final Map singletonCache;
    private final Map aliasMap;

    public AbstractBeanFactory() {
        this.beanPostProcessors = new ArrayList<>();
        this.ignoreDependencyTypes = new HashSet<>();
        this.singletonCache = new HashMap<>();
        this.aliasMap = new HashMap<>();
    }

    public AbstractBeanFactory(BeanFactory parentBeanFactory) {
        this();
        this.parentBeanFactory = parentBeanFactory;
    }

    @Override
    public Object getBean(String name) {
        String beanName = transformedBeanName(name);
        Object sharedInstance = singletonCache.get(beanName);

        if (sharedInstance != null) {
            return getObjectForSharedInstance(name, sharedInstance);
        }

        RootBeanDefinition mergedBeanDefinition = getMergedBeanDefinition(beanName, false);

        if (mergedBeanDefinition.isSingleton()) {
            sharedInstance = createBean(beanName, mergedBeanDefinition);
            return getObjectForSharedInstance(name, sharedInstance);
        }

        return createBean(name, mergedBeanDefinition);
    }

    protected Object createBean(String beanName, RootBeanDefinition mergedBeanDefinition) {
        if (mergedBeanDefinition.getDependsOn() != null) {
            for (int i = 0; i < mergedBeanDefinition.getDependsOn().length; i++) {
                this.getBean(mergedBeanDefinition.getDependsOn()[i]);
            }
        }

        // BeanWrapper
        BeanWrapper instanceWrapper = null;
        if (mergedBeanDefinition.getAutowire() != 3 && mergedBeanDefinition.hasConstructorArgumentValues()) {
//            instanceWrapper = new BeanI
        }

        return null;
    }

    private RootBeanDefinition getMergedBeanDefinition(String beanName, boolean includingAncestors) {

        return null;
    }

    private String transformedBeanName(String name) throws NoSuchBeanDefinitionException {
        if (name == null) {
            throw new NoSuchBeanDefinitionException(name, " cannot get bean with null");
        }

        if (name.startsWith("&")) {
            name = name.substring("&".length());
        }

        String canonicalName = (String) aliasMap.get(name);
        return canonicalName != null ? canonicalName : name;
    }

    protected Object getObjectForSharedInstance(String name, Object beanInstance) {
        String beanName = transformedBeanName(name);
        // todo
        return beanInstance;
    }

    protected RootBeanDefinition getMergedBeanDefinition(String beanName, AbstractBeanDefinition bd) {
        if (bd instanceof RootBeanDefinition) {
            return (RootBeanDefinition) bd;
        }

        if (!(bd instanceof ChildBeanDefinition)) {
            throw new FatalBeanException("BeanDefinition for '" + beanName + "' is neither a RootBeanDefinition or ChildBeanDefinition");
        }

        // ChildBeanDefinition
        ChildBeanDefinition cbd = (ChildBeanDefinition) bd;
        RootBeanDefinition rbd = new RootBeanDefinition(getMergedBeanDefinition(cbd.getParentName(), true));
        rbd.setSingleton(cbd.isSingleton());
        rbd.setLazyInit(cbd.isLazyInit());

        for (int i = 0; i < cbd.getPropertyValues().getPropertyValues().length; i++) {
            rbd.addPropertyValue(cbd.getPropertyValues().getPropertyValues()[i]);
        }
        return rbd;
    }

    @Override
    public Object getBean(String beanName, Class<?> beanClass) {
        return null;
    }

    @Override
    public boolean containsBean(String beanName) {
        return false;
    }

    @Override
    public boolean isSingleton(String beanName) {
        return false;
    }

    @Override
    public boolean getAlias(String beanName) {
        return false;
    }

    @Override
    public void setParentBeanFactory(BeanFactory var1) {

    }

    @Override
    public void registerCustomEditor(Class var1, PropertyEditor var2) {

    }

    @Override
    public void addBeanPostProcessor(BeanPostProcessor var1) {

    }

    @Override
    public void ignoreDependencyType(Class var1) {

    }

    @Override
    public PropertyValues getPropertyValues(String var1) {
        return null;
    }

    @Override
    public void overridePropertyValue(String var1, PropertyValue var2) {

    }

    @Override
    public void registerAlias(String var1, String var2) {

    }

    @Override
    public void registerSingleton(String var1, Object var2) {

    }

    @Override
    public void destroySingletons() {

    }

    public abstract AbstractBeanDefinition getBeanDefinition(String beanName) throws BeansException;
}
